//
//  DKImageSqlite.m
//  搞起
//
//  Created by Alex on 15-3-4.
//  Copyright (c) 2014年 Alex. All rights reserved.
//

#import "DKImageSqlite.h"
#import <AVFoundation/AVFoundation.h>

#pragma mark - 内存对象
@interface DKMemCacheData : NSObject
/**
 *  图片文件大小
 */
@property (nonatomic,assign) long long size;
/**
 *  图片文件
 */
@property (nonatomic,strong) UIImage *image;
@end

@implementation DKMemCacheData
@end
//-----------------------------------------------------------------------------



#pragma mark - 控件栈对象
@interface DKImageData : NSObject
/**
 *  需要显示图片的对象
 */
@property (nonatomic,strong) UIImageView *target;
/**
 *  图片url
 */
@property (nonatomic,strong) NSString *imageUrl;
@end

@implementation DKImageData
@end
//-----------------------------------------------------------------------------





#pragma mark - 回调栈对象
@interface DKImageBlockData : NSObject
/**
 *  图片url
 */
@property (nonatomic,strong) NSString *imageUrl;
/**
 *  下载完成后的回调
 */
@property (nonatomic,copy) DKImageSqliteBlock block;
@end

@implementation DKImageBlockData
@end
//-----------------------------------------------------------------------------





#pragma mark - 下载栈对象
@interface DKImageProcessData : NSObject
/**
 *  图片url
 */
@property (nonatomic,strong) NSString *imageUrl;
/**
 *  下载完成后的回调
 */
@property (nonatomic,copy) DKImageProcessBlock block;
/**
 *  文件大小
 */
@property (nonatomic,assign) long long dataSize;
@end

@implementation DKImageProcessData
@end
//-----------------------------------------------------------------------------















#define DBNAME      @"DKImage.sqlite"
#define ITABLE      @"DKImageBuffer"
#define IID         @"DKImageBufferId"
#define IBUFFER     @"DKImageBUfferBuffer"
@interface DKImageSqlite()<NSURLSessionDataDelegate>
{
    sqlite3 *db;//数据库连接
    dispatch_queue_t queue;//异步线程
    
    NSObject *sqliteSynchronized;//数据库同步标识
    NSObject *apiSynchronized;//对外接口同步标识
    
}
/**
 *  连接session
 */
@property (nonatomic,strong) NSURLSession *session;
/**
 *  图片内存缓存
 */
@property (atomic,strong) NSMutableDictionary *memCache;
/**
 *  控件对象栈
 */
@property (nonatomic,strong) NSMutableArray *targetArray;
/**
 *  回调对象栈
 */
@property (nonatomic,strong) NSMutableArray *completionArray;
/**
 *  下载对象栈
 */
@property (nonatomic,strong) NSMutableArray *processArray;
/**
 *  下载数据缓存
 */
@property (nonatomic,strong) NSMutableDictionary *downloadBufferDic;
/**
 *  记录正在下载的url
 */
@property (nonatomic,strong) NSMutableArray *downloadingArray;
@end


@implementation DKImageSqlite
/**
 *  获取单例
 *
 *  @return 单例
 */
+(instancetype)shareImageSqlite
{
    static DKImageSqlite *instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
        [instance initParameter];
        [instance openDB];
        [instance initDataBase];
    });
    return instance;
}

- (void)initParameter
{
    queue=dispatch_queue_create("DKImageSqlite", 0);
    
    _memCache=[[NSMutableDictionary alloc] init];
    
    _targetArray=[[NSMutableArray alloc] init];
    
    _completionArray=[[NSMutableArray alloc] init];
    
    _processArray=[[NSMutableArray alloc] init];
    
    _downloadBufferDic=[[NSMutableDictionary alloc] init];
    
    _downloadingArray=[[NSMutableArray alloc] init];
    
    _session=[NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
                                           delegate:self
                                      delegateQueue:nil];
    
    sqliteSynchronized=[[NSObject alloc] init];
    
    apiSynchronized=[[NSObject alloc] init];
}

-(void)dealloc
{
    [self closeDB];
}

#pragma mark - 图片内存缓存
/**
 *  获取内存中的图片缓存
 *
 *  @param url 图片url
 *
 *  @return 缓存，若为空则为nil
 */
-(UIImage*)memCacheGetImageWithUrl:(NSString*)url
{
    DKMemCacheData *temp=[_memCache objectForKey:url];
    return temp.image;
}


/**
 *  放置图片文件到内存
 *
 *  @param image    图片文件
 *  @param fileSize 图片大小
 *  @param url      图片url
 */
-(void)memCacheInsertImage:(UIImage*)image
                  fileSize:(long long)fSize
                       url:(NSString*)url
{
    @synchronized(self.memCache)
    {
        if(fSize>1*1024*1024)
        {//大于1M的文件不进内存缓存
            return;
        }
        

        long long totalSize=fSize;
        NSArray* keys=[_memCache allKeys];
        for(int i=0;i<keys.count;i++)
        {
            DKMemCacheData *temp=_memCache[keys[i]];
            totalSize+=temp.size;
        }
        
        
        if(totalSize>20*1024*1024)//内存大于20M，删除一半的缓存
        {
            NSArray* keys=[_memCache allKeys];
            NSMutableArray* deleteKey=[[NSMutableArray alloc] init];
            for(NSInteger i=keys.count-1;i>=keys.count/2;i--)
            {
                [deleteKey addObject:keys[i]];
            }
            [_memCache removeObjectsForKeys:deleteKey];
        }
        
        
        if(image!=nil && url!=nil)
        {
            DKMemCacheData *temp=[[DKMemCacheData alloc] init];
            temp.size=fSize;
            temp.image=image;
            [_memCache setObject:temp forKey:url];
        }
        else
        {
            NSLog(@"图片内存缓存出错");
        }
    }
}







//--------------------------------------------------------------------------------------------------------------------------








#pragma mark - 基本操作
/**
 *  打开数据库
 */
-(void)openDB
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documents = [paths objectAtIndex:0];
    NSString *database_path = [documents stringByAppendingPathComponent:DBNAME];
    
    if (sqlite3_open([database_path UTF8String], &db) != SQLITE_OK) {
        sqlite3_close(db);
        NSLog(@"数据库打开失败");
    }
}
/**
 *  关闭数据库
 */
-(void)closeDB
{
    sqlite3_close(db);
}
/**
 *  执行写操作
 *
 *  @param sql sql语句
 *
 *  @return 执行结果
 */
-(BOOL)execUpdate:(NSString *)sql
{
    char *err;
    if (sqlite3_exec(db, [sql UTF8String], NULL, NULL, &err) != SQLITE_OK) {
        // NSLog(sql);
        sqlite3_close(db);
        // NSLog([NSString stringWithFormat:@"sqlError:%s",err]);
        return NO;
    }
    return YES;
}
/**
 *  执行读操作
 *
 *  @param sql sql语句
 *
 *  @return 结果集
 */
-(sqlite3_stmt *)execQuery:(NSString*)sql
{
    sqlite3_stmt * statement;
    if (sqlite3_prepare_v2(db, [sql UTF8String], -1, &statement, nil) != SQLITE_OK) {
        //   NSLog(sql);
        sqlite3_close(db);
        return nil;
    }
    return statement;
}


#pragma mark - 数据库基本API
/**
 *  初始化数据库
 */
-(void)initDataBase
{
    @synchronized(sqliteSynchronized)
    {
        [self execUpdate:[NSString stringWithFormat:@"create table if not exists %@ (%@ varchar primary key,%@ blob)",ITABLE,IID,IBUFFER]];
    }
}

/**
 *  清空数据
 */
- (void)clearData
{
    @synchronized(sqliteSynchronized)
    {
        [self execUpdate:[NSString stringWithFormat:@"delete from %@",ITABLE]];
    }
}

/**
 *  从数据获取图片
 *
 *  @param url 图片url
 *
 *  @return 图片二进制数据
 */
-(NSData*)imageWithUrl:(NSString*)url
{
    @synchronized(sqliteSynchronized)
    {
        
        NSData* result;
        
        NSString* sql=[NSString stringWithFormat:@"select %@ from %@ where %@='%@'",IBUFFER,ITABLE,IID,url];
        
        sqlite3_stmt * statement=[self execQuery:sql];
        if(statement!=nil)
        {
            if (sqlite3_step(statement) == SQLITE_ROW) {
                int length = sqlite3_column_bytes(statement, 0);
                result=[NSData dataWithBytes:sqlite3_column_blob(statement, 0) length:length];
            }
        }
        sqlite3_finalize(statement);
        
        return result;
    }
}

/**
 *  保存图片到数据库
 *
 *  @param iData 图片二进制流
 *  @param url   图片url
 */
-(void)saveImage:(NSData*)iData withUrl:(NSString*)url
{
    @synchronized(sqliteSynchronized)
    {
        //存储图片
        NSString* sqliteQuery = [NSString stringWithFormat:@"insert into %@ (%@,%@) VALUES ('%@',?)",ITABLE,IID,IBUFFER,url];
        sqlite3_stmt* statement;
        if( sqlite3_prepare_v2(db, [sqliteQuery UTF8String], -1, &statement, NULL) == SQLITE_OK )
        {
            sqlite3_bind_blob(statement, 1, [iData bytes], (int)[iData length], SQLITE_TRANSIENT);
            sqlite3_step(statement);
        }
        sqlite3_finalize(statement);
    }
}







//--------------------------------------------------------------------------------------------------------------------------








#pragma mark - 函数API
/**
 *  控件栈中插入新数据
 *
 *  @param target           目标imageView
 *  @param url              图片url
 */
- (void)updateStackWithTarget:(UIImageView*)target
                         url:(NSString*)url
{
    BOOL isFind=NO;
    for(int i=0;i<self.targetArray.count;i++)
    {
        DKImageData* temp=self.targetArray[i];
        if(temp.target==target)
        {//该对象已经加载，则将要加载的对象的图片更新
            isFind=YES;
            temp.imageUrl=url;
            break;
        }
    }
    
    if(!isFind)
    {//新对象
        DKImageData* temp=[[DKImageData alloc] init];
        temp.target=target;
        temp.imageUrl=url;
        [self.targetArray addObject:temp];
    }
}


/**
 *  回调栈中插入新数据
 *
 *  @param block            回调
 *  @param url              图片url
 */
-(void)updateStackWithBlock:(DKImageSqliteBlock)block
                        url:(NSString*)url
{
    DKImageBlockData* temp=[[DKImageBlockData alloc] init];
    [temp setBlock:block];
    temp.imageUrl=url;
    [self.completionArray addObject:temp];
}


/**
 *  下载栈中插入新数据
 *
 *  @param block            回调
 *  @param url              图片url
 */
-(void)updateStackWithProcessBlock:(DKImageProcessBlock)block
                               url:(NSString*)url
{
    DKImageProcessData *temp=[[DKImageProcessData alloc] init];
    [temp setBlock:block];
    [temp setImageUrl:url];
    [temp setDataSize:-1];
    
    for(int i=0;i<self.processArray.count;i++)
    {
        DKImageProcessData *oTemp=self.processArray[i];
        if([oTemp.imageUrl isEqualToString:url])
        {
            temp.dataSize=oTemp.dataSize;
            
            NSMutableData* bufferData=self.downloadBufferDic[url];
            [self runOnMain:^{
                temp.block(temp.dataSize,bufferData.length,NO);
            }];
            break;
        }
    }
    
    [self.processArray addObject:temp];
}


/**
 *  更新下载栈中的对象总大小
 *
 *  @param url       图片url
 *  @param totalSize 文件总大小
 */
- (void)updateProcessBlock:(NSString *)url
                 totalSize:(long long)totalSize
{
    for(int i=(int)self.processArray.count-1;i>=0;i--)
    {
        DKImageProcessData* temp=self.processArray[i];
        if([temp.imageUrl isEqualToString:url])
        {
            temp.dataSize=totalSize;
        }
    }
}

/**
 *  更新正在下载的url列表
 *
 *  @param url 图片url
 *
 *  @return url插入是否存在，若存在，则为NO
 */
- (BOOL)updateDownloadingStack:(NSString *)url
{
    for(int i=0;i<self.downloadingArray.count;i++)
    {
        NSString *tempUrl=self.downloadingArray[i];
        if([tempUrl isEqualToString:url])
        {
            return NO;
        }
    }
    [self.downloadingArray addObject:url];
    return YES;
}

- (void)removeDonwloadingStack:(NSString *)url
{
    for(int i=(int)self.downloadingArray.count-1;i>=0;i--)
    {
        NSString *tempUrl=self.downloadingArray[i];
        if([tempUrl isEqualToString:url])
        {
            [self.downloadingArray removeObjectAtIndex:i];
        }
    }
}


/**
 *  图片下载过程中唤起下载栈中的对象
 *
 *  @param url         图片url
 *  @param rDataLength 收到的数据长度
 *  @param finish      是否结束
 */
- (void)wakeProcessBlock:(NSString *)url
        hasReceiveLength:(long long)rDataLength
                isFinish:(BOOL)finish
{
    for(int i=(int)self.processArray.count-1;i>=0;i--)
    {
        DKImageProcessData* temp=self.processArray[i];
        if([temp.imageUrl isEqualToString:url])
        {
            [self runOnMain:^{
                temp.block(temp.dataSize,rDataLength,finish);
            }];
        }
    }
}


/**
 *  图片下载好后唤起栈中的对象
 *
 *  @param url   图片url
 *  @param image 图片文件
 */
-(void)setImageWithUrl:(NSString*)url
                 image:(UIImage*)image
{
    //将图片设置到对象上
    for(int i=(int)self.targetArray.count-1;i>=0;i--)
    {
        DKImageData* temp=self.targetArray[i];
        if([temp.imageUrl isEqualToString:url])
        {
            if(image!=nil)
            {
                [self runOnMain:^{
                    [temp.target setImage:image];
                }];
            }
            [self.targetArray removeObjectAtIndex:i];
        }
    }
    
    //唤醒需要完成的block
    for(int i=(int)self.completionArray.count-1;i>=0;i--)
    {
        DKImageBlockData* temp=self.completionArray[i];
        if([temp.imageUrl isEqualToString:url])
        {
            [self runOnMain:^{
                [temp block](image);
            }];
            [self.completionArray removeObjectAtIndex:i];
        }
    }
    
    //删除下载中的block
    for(int i=(int)self.processArray.count-1;i>=0;i--)
    {
        DKImageProcessData* temp=self.processArray[i];
        if([temp.imageUrl isEqualToString:url])
        {
            [self.processArray removeObjectAtIndex:i];
        }
    }
}

/**
 *  移除对当前target的图片设置
 *
 *  @param target 目标对象
 */
- (void)removeTarget:(UIImageView *)target
{
    for(int i=(int)self.targetArray.count-1;i>=0;i--)
    {
        DKImageData* temp=self.targetArray[i];
        if(temp.target==target)
        {
            [self.targetArray removeObjectAtIndex:i];
        }
    }
}




#pragma mark - NSURLSessionDataDelegate
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
    NSString* url=dataTask.currentRequest.URL.absoluteString;
    if(url==nil)
    {
        return;
    }
    
    
    NSHTTPURLResponse *httpResponse=(NSHTTPURLResponse *)response;
    if(httpResponse && [httpResponse respondsToSelector:@selector(allHeaderFields)])
    {
        NSDictionary *headerFields=[httpResponse allHeaderFields];
        long long total=[[headerFields objectForKey:@"Content-Length"] longLongValue];
        [self updateProcessBlock:url
                       totalSize:total];
        [self wakeProcessBlock:url
              hasReceiveLength:0
                      isFinish:NO];
    }
    else
    {
        [self updateProcessBlock:url
                       totalSize:-1];
        [self wakeProcessBlock:url
              hasReceiveLength:0
                      isFinish:NO];
    }
    
    completionHandler(NSURLSessionResponseAllow);
}


- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
    NSString* url=dataTask.currentRequest.URL.absoluteString;
    if(url==nil)
    {
        return;
    }
    
    if(self.downloadBufferDic[url]==nil)
    {
        NSMutableData* bufferData=[[NSMutableData alloc] initWithData:data];
        [self.downloadBufferDic setObject:bufferData forKey:url];
        [self wakeProcessBlock:url
              hasReceiveLength:bufferData.length
                      isFinish:NO];
    }
    else
    {
        NSMutableData* bufferData=self.downloadBufferDic[url];
        [bufferData appendData:data];
        [self.downloadBufferDic setObject:bufferData forKey:url];
        [self wakeProcessBlock:url
              hasReceiveLength:bufferData.length
                      isFinish:NO];
    }
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    NSString* url=task.currentRequest.URL.absoluteString;
    if(url==nil)
    {
        return;
    }
    
    
    NSData* asyncData=self.downloadBufferDic[url];
    [self.downloadBufferDic removeObjectForKey:url];
    
    if(asyncData==nil || [[UIImage alloc] initWithData:asyncData]==nil || error!=nil)
    {
        NSLog(@"DKImageSqlite:下载图片出错-%@",url);
        [self wakeProcessBlock:url
              hasReceiveLength:-1
                      isFinish:YES];
        
        @synchronized(apiSynchronized)
        {
            [self setImageWithUrl:url image:nil];
        }
        
    }
    else
    {//图片成功下载＋缓存到内存＋保存到数据库＋图片显示
        [self wakeProcessBlock:url
              hasReceiveLength:asyncData.length
                      isFinish:YES];

        [self saveImage:asyncData withUrl:url];
        
        UIImage* temp=[UIImage imageWithData:asyncData];
        @synchronized(apiSynchronized)
        {
            [self setImageWithUrl:url image:temp];
            [self memCacheInsertImage:temp fileSize:asyncData.length url:url];
        }
    }
    
    @synchronized(apiSynchronized)
    {
        [self removeDonwloadingStack:url];
    }
}




#pragma mark - API
/**
 *  异步下载图片
 *
 *  @param url              图片url
 *  @param placeHodler      默认图片
 *  @param target           目标imageView
 *  @param process          下载中的回调
 *  @param enableDownload   是否允许从服务端下载
 *  @param completion       完成后的回调
 */
-(void)loadImage:(NSString*)url
     placeHolder:(UIImage*)placeHolder
          target:(UIImageView*)target
         process:(DKImageProcessBlock)process
  enableDownload:(BOOL)enableDownload
      completion:(DKImageSqliteBlock)completion
{
    //设置默认图片
    if(target!=nil)
    {
        @synchronized(apiSynchronized)
        {
            [self removeTarget:target];
        }
        [target setImage:placeHolder];
    }
    
    //检查url
    if(url.length==0)
    {
        return;
    }
    
    
    //---------------------------------------------------------
    //从内存中获取该图片
    UIImage* memPhoto=[self memCacheGetImageWithUrl:url];
    if(memPhoto!=nil)
    {
        if(target!=nil)
        {
            [target setImage:memPhoto];
        }
        if(completion!=nil)
        {
            completion(memPhoto);
        }
        return;
    }
    
    
    //---------------------------------------------------------
    @synchronized(apiSynchronized)
    {
        if(target!=nil)
        {
            [self updateStackWithTarget:target url:url];
        }
        if(process!=nil)
        {
            [self updateStackWithProcessBlock:process url:url];
        }
        if(completion!=nil)
        {
            [self updateStackWithBlock:completion url:url];
        }
    }
    dispatch_async(queue, ^(void)
                   {
                       //---------------------------------------------------------
                       //去数据库检索
                       NSData* sqliteData=[self imageWithUrl:url];
                       if(sqliteData!=nil)
                       {
                           //数据库中有，则放入内存缓存中＋显示图片
                           UIImage* sqlitePhoto=[UIImage imageWithData:sqliteData];
                           
                           [self runOnMain:^{
                               @synchronized(apiSynchronized)
                               {
                                   [self setImageWithUrl:url image:sqlitePhoto];
                                   [self memCacheInsertImage:sqlitePhoto fileSize:sqliteData.length url:url];
                               }
                               
                           }];
                           
                       }
                       
                       //---------------------------------------------------------
                       //通过网络下载
                       else if(enableDownload)
                       {
                           @synchronized(apiSynchronized)
                           {
                               if(![self updateDownloadingStack:url])
                               {
                                   return;
                               }
                           }

                           if([url rangeOfString:@".mp4"].location==NSNotFound)
                           {//图片
                               NSURLRequest* request=[[NSURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
                               NSURLSessionDataTask* task=[self.session dataTaskWithRequest:request];
                               [task resume];
                           }
                           else
                           {//视频
                               [self updateProcessBlock:url
                                              totalSize:-1];
                               [self wakeProcessBlock:url
                                     hasReceiveLength:0
                                             isFinish:NO];
                               //------------------------------------------------
                               //利用视频流获取第一帧
                               AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:[NSURL URLWithString:url] options:nil];
                               AVAssetImageGenerator *gen = [[AVAssetImageGenerator alloc] initWithAsset:asset];
                               gen.appliesPreferredTrackTransform = YES;
                               CMTime time = CMTimeMakeWithSeconds(0, 600);
                               NSError *error = nil;
                               CMTime actualTime;
                               CGImageRef image = [gen copyCGImageAtTime:time actualTime:&actualTime error:&error];
                               //------------------------------------------------
                               
                               UIImage *thumbImg = [[UIImage alloc] initWithCGImage:image];
                               if(thumbImg==nil)
                               {
                                   NSLog(@"DKImageSqlite:下载图片出错-%@",url);
                                   [self wakeProcessBlock:url
                                         hasReceiveLength:-1
                                                 isFinish:YES];
                                   @synchronized(apiSynchronized) {
                                       [self setImageWithUrl:url image:placeHolder];
                                   }
                               }
                               else
                               {//图片成功下载＋缓存到内存＋保存到数据库＋图片显示
                                   [self wakeProcessBlock:url
                                         hasReceiveLength:0
                                                 isFinish:YES];
                                   
                                   NSData *photoData=UIImageJPEGRepresentation(thumbImg, 1.0);
                                   [self saveImage:photoData withUrl:url];
                                   
                                   @synchronized(apiSynchronized)
                                   {
                                       [self setImageWithUrl:url image:thumbImg];
                                       [self memCacheInsertImage:thumbImg fileSize:photoData.length url:url];
                                   }
                               }
                               
                               @synchronized(apiSynchronized)
                               {
                                   [self removeDonwloadingStack:url];
                               }
                           }
                       }
                       
                   });
}




/**
 *  在主线程运行
 *
 *  @param block 主线程运行的block
 */
-(void)runOnMain:(void(^)())block
{
    dispatch_async(dispatch_get_main_queue(), ^{
        block();
    });
}


/**
 *  清空数据
 */
+(void)clearData
{
    [[DKImageSqlite shareImageSqlite] clearData];
}


@end
